using System;
using System.Collections.Generic;
using System.Text;

using MyAI.NeuralNetworkLib.Specification;
using System.Xml;

namespace MyAI.NeuralNetworkLib.Implementation
{
    public class MultiLayerFeedForwardNeuralNetwork : IMultiLayerFeedForwardNeuralNetwork
    {
        #region Vars
        private NeuralLayerTypes _nlType;
        private NeuronTypes _nType;
        private ActivationFunctionTypes _afType;
        private List<double> _afPrms;
        private List<INeuralLayer> _layers;
        private int _inputVectorDimention;
        private LearningMethods _lMethod;
        private ILearningStrategy _lStratagy;
        #endregion

        #region Constructor
        public MultiLayerFeedForwardNeuralNetwork()
        {
            _layers = new List<INeuralLayer>();
        }
        #endregion

        #region IMultiLayerFeedForwardNeuralNetwork Members

        public void GenerateNeuralNetwork(int inputVectorDimention, List<int> structure, 
            NeuralLayerTypes nlType, NeuronTypes nType,
            ActivationFunctionTypes afType, List<double> afPrms, 
            LearningMethods lMethod)
        {
            if (structure.Count == 0)
            {
                throw new InvalidOperationException("Cant create network with 0 layers");
            }
            _nlType = nlType;
            _nType = nType;
            _afType = afType;
            _afPrms = afPrms;
            _inputVectorDimention = inputVectorDimention;
            _lMethod = lMethod;

            _lStratagy = LearningStrategyFactory.Instance.CreateLearningStrategy(lMethod);

            foreach (int k in structure)
            {
                if (k < 1)
                {
                    throw new InvalidOperationException("Cant create layer with 0 neurons");
                }
                INeuralLayer tmpLayer = NeuralLayerFactory.Instance.CreateLayer(_nlType);
                if (_layers.Count == 0)
                {
                    tmpLayer.GenerateLayer(_inputVectorDimention, k, _nType,
                        _afType, _afPrms);
                }
                else
                {
                    tmpLayer.GenerateLayer(_layers[_layers.Count - 1].GetNeurons().Count, k, _nType,
                        _afType, _afPrms);
                }
                _layers.Add(tmpLayer);
            }
        }

        public int LayersNumber
        {
            get 
            {
                return _layers.Count;
            }
        }

        public List<INeuralLayer> Layers
        {
            get
            {
                return _layers;
            }
        }

        public List<double> AFParams
        {
            get
            {
                return _afPrms;
            }
        }

        public int OutputDimention
        {
            get 
            {
                return _layers[_layers.Count - 1].GetNeurons().Count;
            }
        }

        public int InputDimention
        {
            get 
            {
                return _inputVectorDimention;
            }
        }

        public List<double> Transform(List<double> inputVector)
        {
            if (inputVector.Count != _inputVectorDimention)
            {
                throw new ArgumentOutOfRangeException("inputVector", "Input vector has different dimention");
            }
            List<double> tmpVector = new List<double>();
            foreach (INeuralLayer l in _layers)
            {
                if (tmpVector.Count == 0)
                {
                    tmpVector = l.CalculateOutput(inputVector);
                }
                else
                {
                    tmpVector = l.CalculateOutput(tmpVector);
                }
            }
            return tmpVector;
        }

        public ILearningStrategy LearningStrategy
        {
            get
            {
                return _lStratagy;
            }
        }

        public ActivationFunctionTypes AFType
        {
            get
            {
                return _afType;
            }
        }

        #endregion
    }
}
